/*******************************************************************************
 * Copyright (c) 2014, 2015 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *    Allan Stockdill-Mander - initial API and implementation and/or initial documentation
 *    Ian Craggs - convert to FreeRTOS
 *******************************************************************************/

#include "MQTTLiteOS.h"

#ifdef LITEOS
#define MQTT_TASK_STACK_SIZE 2048
#define MQTT_TASK_PRIO             39

static u_int32_t mqtt_demo_task_stk[MQTT_TASK_STACK_SIZE];
#endif

int ThreadStart(Thread* thread, void (*entry)(void*), void* arg)
{
	int rc = 0;
#ifdef LITEOS
	tls_os_task_create(NULL, "MQTTTask", fn, arg, (void*)mqtt_demo_task_stk, sizeof(mqtt_demo_task_stk), MQTT_TASK_PRIO, 0);
#else
#ifdef LINUX
    rc = pthread_create(&thread->task, NULL, entry, arg);
#endif
#endif
	return rc;
}

void TimerInit(Timer* timer)
{
	timer->end_time = (struct timeval){0, 0};
}

int64_t TimerToMs(Timer *timer)
{
	return (int64_t)(timer->end_time.tv_sec) * 1000 + (int64_t)(timer->end_time.tv_usec)/1000;
}

int GetTimeOfday(struct timeval *tv, struct timezone *tz)
{
#ifdef LITEOS
	uint32_t ms = tls_os_get_time() * 1000/HZ;
	tv->tv_sec = ms / 1000;
	tv->tv_usec = (ms % 1000) *1000;
#else
#ifdef LINUX
    gettimeofday(tv, tz);
#endif
#endif
	return 0;
}

int64_t now()
{
	struct timeval tv = {0};
	GetTimeOfday(&tv, NULL);
	return TimevalToMs(&tv);
}

uint32_t getTimeInSeconds()
{
    return now()/1000;
}

int64_t TimevalToMs(struct timeval *a)
{
	return (int64_t)a->tv_sec * 1000 + (int64_t)a->tv_usec /1000;
}

struct timeval MsToTimeval(int64_t ms)
{
	struct timeval a = {
		.tv_sec = ms/1000,
		.tv_usec = ms %1000 * 1000,
	};
	return a;
}

static void TimerAdd(struct timeval *a, struct timeval *b, struct timeval *res)
{
	int64_t c = TimevalToMs(a) + TimevalToMs(b);
	*res = MsToTimeval(c);
}

static void TimerSub(struct timeval *a, struct timeval *b, struct timeval *res)
{
	int64_t c = TimevalToMs(a) - TimevalToMs(b);
	*res = MsToTimeval(c);
}

char TimerIsExpired(Timer* timer)
{
	struct timeval now, res;
	GetTimeOfday(&now, NULL);
	TimerSub(&timer->end_time, &now, &res);
	char ret = (int64_t)res.tv_sec < 0 || ((int64_t)res.tv_sec == 0 && (int64_t)res.tv_usec <= 0);
	return ret;
}


void TimerCountdownMS(Timer* timer, unsigned int timeout)
{
	struct timeval now;
	GetTimeOfday(&now, NULL);
	struct timeval interval = {timeout / 1000, (timeout % 1000) * 1000};
	TimerAdd(&now, &interval, &timer->end_time);
}


void TimerCountdown(Timer* timer, unsigned int timeout)
{
	struct timeval now;
	GetTimeOfday(&now, NULL);
	struct timeval interval = {timeout, 0};
	TimerAdd(&now, &interval, &timer->end_time);
}


int TimerLeftMS(Timer* timer)
{
	struct timeval now, res;
	GetTimeOfday(&now, NULL);
	TimerSub(&timer->end_time, &now, &res);
	int64_t ret = TimevalToMs(&res);
	return ret;
}

void MutexInit(Mutex* mutex)
{
	memset(mutex, 0, sizeof(Mutex));
#ifdef LITEOS
	tls_os_mutex_create(0, &mutex->mutex);
#else
#ifdef LINUX
    pthread_mutex_init(&mutex->mutex, NULL);
#endif
#endif
}

void MutexUninit(Mutex *mutex)
{
#ifdef LITEOS
	tls_os_mutex_delete(mutex->mutex);
	mutex->mutex = NULL;
#else
#ifdef LINUX
    pthread_mutex_destroy(&mutex->mutex);
#endif
#endif
}

int MutexLock(Mutex* mutex)
{
#ifdef LITEOS
    return tls_os_mutex_acquire(mutex->mutex, 0) == TLS_OS_SUCCESS ? 0 : 1;
#else
#ifdef LINUX
    return pthread_mutex_lock(&mutex->mutex);
#endif
#endif
}

int MutexUnlock(Mutex* mutex)
{
#ifdef LITEOS
	return tls_os_mutex_release(mutex->mutex) == TLS_OS_SUCCESS ? 0 : 1;
#else
#ifdef LINUX
    return pthread_mutex_unlock(&mutex->mutex);
#endif
#endif
}

#ifdef LITEOS
int FreeRTOS_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
    (void)(timeout_ms);
	int recvLen = 0;

	do
	{
		int rc = 0;
		rc = recv(n->my_socket, buffer + recvLen, len - recvLen, 0);
		if (rc > 0)
			recvLen += rc;
		else if (rc < 0)
		{
			recvLen = rc;
			break;
		}
	} while (recvLen < len);

	return recvLen;
}


int FreeRTOS_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
    (void)(timeout_ms);
	int sentLen = 0;
	do
	{
		int rc = 0;

		rc = send(n->my_socket, buffer + sentLen, len - sentLen, 0);
		if (rc > 0)
			sentLen += rc;
		else if (rc < 0)
		{
			sentLen = rc;
			break;
		}
	} while (sentLen < len);

	return sentLen;
}
#else
#ifdef LINUX
int linux_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
	struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
	if ((int)interval.tv_sec < 0 || ((int)interval.tv_sec == 0 && (int)interval.tv_usec <= 0))
	{
		interval.tv_sec = 0;
		interval.tv_usec = 100*1000;
	}

//	 setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));

	int bytes = 0;
	while (bytes < len)
	{
		fd_set rset;
		struct timeval tv = interval;
		FD_ZERO(&rset);
		FD_SET(n->my_socket, &rset);
		int rc = select(n->my_socket+1, &rset, NULL, NULL, &tv);
		if(rc == 0) {
			bytes = 0;
			break;
		} else if(rc <0) {
			bytes = -1;
			break;
		}
		rc = recv(n->my_socket, &buffer[bytes], (size_t)(len - bytes), 0);
		if (rc == -1)
		{
			if (errno != EAGAIN && errno != EWOULDBLOCK)
			  bytes = -1;
			break;
		}
		else if (rc == 0)
		{
			bytes = 0;
			break;
		}
		else
			bytes += rc;
	}
	// PRINTF("try to read %d recved return %d bytes\n", len, bytes);
	return bytes;
}


int linux_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
	struct timeval tv;

	tv.tv_sec = 0;  /* 30 Secs Timeout */
	tv.tv_usec = timeout_ms * 1000;  // Not init'ing this can cause strange errors

	setsockopt(n->my_socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,sizeof(struct timeval));
	int	rc = send(n->my_socket, buffer, len, 0);
	return rc;
}
#endif
#endif

void NetworkInit(Network* n)
{
	n->my_socket = -1;
#ifdef LITEOS
    n->mqttread = FreeRTOS_read;
	n->mqttwrite = FreeRTOS_write;
#else
#ifdef LINUX
	n->mqttread = linux_read;
    n->mqttwrite = linux_write;
#endif
#endif
}


int NetworkConnect(Network* n, char* addr, int port)
{
	int type = SOCK_STREAM;
	int rc = -1;
	sa_family_t family = AF_INET;
    struct sockaddr_in address;
#ifdef LINUX
    struct in_addr a = {0};
	if(inet_aton(addr, &a) != 0) {
		address.sin_port = htons(port);
		address.sin_family = family = AF_INET;
		address.sin_addr.s_addr = a.s_addr;
		rc = 0;
	}
	if (rc == 0)
#else
	address.sin_port = htons(port);
	address.sin_family = family = AF_INET;
	address.sin_addr.s_addr = inet_addr(addr);
#endif
	{
		n->my_socket = socket(family, type, 0);
		PRINTF("socket %d\n", n->my_socket);
		if (n->my_socket != -1)
			rc = connect(n->my_socket, (struct sockaddr*)&address, sizeof(address));
		else
			rc = -1;
		PRINTF("socket %d connect return %d\n", n->my_socket, rc);
	}

	return rc;
}


void NetworkDisconnect(Network* n)
{
	PRINTF("close socket %d\n", n->my_socket);
	if(n->my_socket >=0 ) {
#ifdef LITEOS
		closesocket(n->my_socket);
#else
#ifdef LINUX
	    close(n->my_socket);
#endif
#endif
		n->my_socket = -1;		
	}
}
